03 万丈高楼平地起 - Bash 的基础语法 -2
bash 循环
for 循环
for循环是一种常用的循环结构,用于遍历一个列表或范围,并对每个元素执行一组命令。
bash
for variable in list; do
command1
command2
...
donevariable:循环变量,用于存储当前遍历的元素。list:要遍历的列表或范围。command1、command2等:在每次循环中要执行的命令。
| 用法类型 | 示例代码 | 说明 |
|---|---|---|
| 遍历列表 | for item in item1 item2 item3; do echo $item; done | 依次遍历 item1、item2、item3。 |
| 遍历文件 | for file in *.txt; do echo $file; done | 遍历当前目录下所有 .txt 文件。 |
C 风格的 for 循环 | for ((i=0; i<5; i++)); do echo $i; done | 使用 C 风格语法,i 从 0 遍历到 4。 |
使用 seq 生成序列 | for i in $(seq 1 5); do echo $i; done | 使用 seq 生成 1 到 5 的数字序列。 |
| 遍历数组 | for fruit in "${array[@]}"; do echo $fruit; done | 遍历数组 array 中的每个元素。 |
| 使用命令替换的循环 | for filename in $(ls *.sh); do echo $filename; done | 使用 $(ls *.sh) 获取所有 .sh 文件并遍历。 |
遍历数组列表
bash
➜ 0902 git:(main) cat for_various_lists.sh
#!/bin/bash
# 遍历字符串列表
for item in one two three; do
echo "Item: $item"
done
# 遍历数字列表
for VARIABLE in 1 2 3 4 5; do
echo "Number: $VARIABLE"
done
# 定义一个数组
fruits=("Apple" "Banana" "Cherry" "Date" "Elderberry")
# 遍历数组
for fruit in "${fruits[@]}"
do
echo "Fruit: $fruit"
done
# 定义一个字符串列表
colors="Red Green Blue Yellow"
# 遍历字符串列表
for color in $colors
do
echo "Color: $color"
done
➜ 0902 git:(main) bash for_various_lists.sh
Item: one
Item: two
Item: three
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
Fruit: Apple
Fruit: Banana
Fruit: Cherry
Fruit: Date
Fruit: Elderberry
Color: Red
Color: Green
Color: Blue
Color: Yellow遍历文件
bash
➜ 0902 git:(main) cat for_files.sh
#!/bin/bash
# 创建 5 个测试文件
touch file[1-5].txt
# 遍历当前目录下的所有 .txt 文件
for file in *.txt; do
echo "Processing $file file..."
done
# 删除所有 .txt 文件
rm -f *.txt
# 遍历 ls 命令的输出,列出所有 .sh 文件
for file in $(ls *.sh); do
echo "Found file: $file"
done
➜ 0902 git:(main) ✗ bash for_files.sh
Processing file1.txt file...
Processing file2.txt file...
Processing file3.txt file...
Processing file4.txt file...
Processing file5.txt file...
Found file: for_c_style.sh
Found file: for_files.sh
Found file: for_infinite.sh
Found file: for_nested.sh
Found file: for_ranges.sh
Found file: for_seq.sh
Found file: for_various_lists.sh遍历数字或字母范围
bash
➜ 0902 git:(main) cat for_ranges.sh
#!/bin/bash
# 遍历 1 到 5 的数字
for i in {1..5}; do
echo "Number: $i"
done
# 遍历字母 a 到 c
for letter in {a..c}; do
echo "Letter: $letter"
done
➜ 0902 git:(main) bash for_ranges.sh
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
Letter: a
Letter: b
Letter: c使用 C 风格的 for 循环
bash
➜ 0902 git:(main) cat for_c_style.sh
#!/bin/bash
# 使用 C 风格的 for 循环
for ((i = 0; i < 5; i++)); do
echo "Index: $i"
done
➜ 0902 git:(main) bash for_c_style.sh
Index: 0
Index: 1
Index: 2
Index: 3
Index: 4使用 seq 生成序列
bash
➜ 0902 git:(main) cat for_seq.sh
#!/bin/bash
# 使用 seq 生成 1 到 5 的数字序列
for i in $(seq 1 5); do
echo "Number: $i"
done
# 遍历 1 到 10 的数字,步长为 2
for i in $(seq 1 2 10); do
echo "Number: $i"
done
➜ 0902 git:(main) bash for_seq.sh
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
Number: 1
Number: 3
Number: 5
Number: 7
Number: 9嵌套循环
bash
➜ 0902 git:(main) cat for_nested.sh
#!/bin/bash
# 外层循环
for i in {1..3}; do
# 内层循环
for j in {A..C}; do
echo "Outer loop: $i, Inner loop: $j"
done
done
➜ 0902 git:(main) bash for_nested.sh
Outer loop: 1, Inner loop: A
Outer loop: 1, Inner loop: B
Outer loop: 1, Inner loop: C
Outer loop: 2, Inner loop: A
Outer loop: 2, Inner loop: B
Outer loop: 2, Inner loop: C
Outer loop: 3, Inner loop: A
Outer loop: 3, Inner loop: B
Outer loop: 3, Inner loop: Cfor 实现无限循环
bash
➜ 0902 git:(main) cat for_infinite.sh
#!/bin/bash
# 无限循环
for (( ; ; )); do
echo "This is an infinite loop!"
# 为避免陷入死循环,可以加入 `sleep` 让程序休眠
sleep 1
done
➜ 0902 git:(main) bash for_infinite.sh
This is an infinite loop!
This is an infinite loop!
This is an infinite loop!
This is an infinite loop!
^Cwhile 循环
while循环用于在给定的条件为真时反复执行一组命令。
bash
while [ condition ]; do
# commands
donecondition:条件表达式,条件为真时,循环继续执行。当条件变为假时,循环结束。do:开始执行循环体的命令。done:结束循环体。
数值比较
- 使用
[ ]或(( ))进行数值比较。 - 常用的数值比较运算符:
| 符号 | 描述 |
|---|---|
-lt (less than) | 小于 |
-le (less or equal) | 小于等于 |
-eq (equal) | 等于 |
-ne (not equal) | 不等于 |
-gt (greater than) | 大于 |
-ge (greater or equal) | 大于等于 |
bash
➜ 0902 git:(main) cat while_numeric_comparison_loops.sh
#!/bin/bash
counter=1
while [ $counter -le 5 ]; do # 当 counter 小于等于 5 时继续循环
echo "Counter: $counter"
((counter++)) # 计数器加 1
done
number=3
while [ $number -gt 0 ]; do # 当 number 大于 0 时继续循环
echo "Number: $number"
((number--))
done
index=0
while ((index < 5)); do # 当 index 小于 5 时继续循环
echo "Index: $index"
((index++))
done
➜ 0902 git:(main) bash while_numeric_comparison_loops.sh
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
Number: 3
Number: 2
Number: 1
Index: 0
Index: 1
Index: 2
Index: 3
Index: 4字符串比较
- 使用
[ ]进行字符串比较。 - 常用的字符串比较运算符:
| 符号 | 描述 |
|---|---|
= | 等于 |
!= | 不等于 |
-z | 字符串为空 |
-n | 字符串不为空 |
bash
➜ 0902 git:(main) cat while_string_comparison_loops.sh
#!/bin/bash
input="yes"
while [ "$input" = "yes" ]; do # 当输入是 "yes" 时继续循环
echo "Do you want to continue? (yes/no)"
read input
done
string="hello"
while [ "$string" != "bye" ]; do # 当 string 不等于 "bye" 时循环
echo "String is: $string"
string="bye" # 改变条件,以避免无限循环
done
➜ 0902 git:(main) bash while_string_comparison_loops.sh
Do you want to continue? (yes/no)
yes
Do you want to continue? (yes/no)
no
String is: hello文件状态
| 选项 | 描述 |
|---|---|
-e (exists) | 文件存在 |
-f (file) | 文件存在且是普通文件 |
-d (directory) | 文件存在且是目录 |
-r (readable) | 文件可读 |
-w (writable) | 文件可写 |
-x (executable) | 文件可执行 |
bash
➜ 0902 git:(main) cat while_file_status_check.sh
#!/bin/bash
filename="test.txt"
index=0
while [[ ! -e $filename && $index -le 3 ]]; do
echo "Waiting for file $filename to be created..."
sleep 2 # 等待 2 秒后再检查
((index++))
done
# 如果 index 大于 3,则说明文件不存在
if [ $index -ge 3 ]; then
echo "File $filename not found after 6 seconds."
fi
echo "hello data.txt" >data.txt
while [ -f 'data.txt' ]; do
echo "File $filename exists."
cat 'data.txt'
break # 避免无限循环,使用 break 跳出循环
done
rm -f data.txt
➜ 0902 git:(main) bash while_file_status_check.sh
Waiting for file test.txt to be created...
Waiting for file test.txt to be created...
Waiting for file test.txt to be created...
Waiting for file test.txt to be created...
File test.txt not found after 6 seconds.
File test.txt exists.
hello data.txt逻辑运算符
&&:逻辑与(and)||:逻辑或(or)!:逻辑非(not)
bash
➜ 0902 git:(main) cat while_logical_operators_loops.sh
#!/bin/bash
# 逻辑与:当 count 小于等于 3 且文件存在时循环
count=1
if [ ! -f "data.txt" ]; then # 如果 data.txt 不存在,则创建它
touch data.txt
fi
# 使用 [[ ]] 进行条件判断
while [[ $count -le 3 && -f "data.txt" ]]; do
echo "Count: $count"
((count++))
done
rm -f data.txt
# 逻辑或:当 index 大于 12 或不等于 15 时循环
index=18
while [[ $count -gt 12 || $index -ne 15 ]]; do
echo "Index: $index"
((index--))
done
# 逻辑非:当 num 不等于 4 时循环
num=6
while [ ! $num -eq 4 ]; do
echo "Num: $num"
((num--))
done
➜ 0902 git:(main) bash while_logical_operators_loops.sh
Count: 1
Count: 2
Count: 3
Index: 18
Index: 17
Index: 16
Num: 6
Num: 5无限循环 true
Bash 还可以使用 true 和 false 控制循环:
true:始终返回true,形成无限循环。false:始终返回false,不执行循环。
bash
➜ 0902 git:(main) cat while_infinite_loop_with_true.sh
#!/bin/bash
# 使用 true 实现无限循环
while true; do # 无限循环,除非使用 break 或 Ctrl+C 停止
echo "This is an infinite loop! Press [CTRL+C] to stop."
sleep 1
done
➜ 0902 git:(main) bash while_infinite_loop_with_true.sh
This is an infinite loop! Press [CTRL+C] to stop.
This is an infinite loop! Press [CTRL+C] to stop.
This is an infinite loop! Press [CTRL+C] to stop.
This is an infinite loop! Press [CTRL+C] to stop.
^C使用命令作为条件
while 循环条件还可以是一个命令或命令的组合。如果命令执行成功(返回状态码为 0),条件为真,循环继续执行。
bash
➜ 0902 git:(main) cat while_command_output_condition_loop.sh
#!/bin/bash
# ping baidu.com 成功时返回 0,失败时返回非 0 值
while ping -c 1 www.baidu.com >/dev/null; do # 只要能 ping 通 baidu.com,继续循环
echo "Baidu is reachable"
sleep 2
echo "192.0.2.1 www.baidu.com" | sudo tee -a /etc/hosts
done
echo "Baidu is not reachable anymore"
sudo sed -i '/192.0.2.1 www.baidu.com/d' /etc/hosts
# 使用命令的退出状态码作为条件
if [ -f "data.txt" ]; then
rm -f data.txt
fi
echo "hello data.txt" >data.txt
while grep -q "hello" data.txt; do
echo "Found hello in data.txt. data.txt contents:"
cat data.txt
break
done
rm -f data.txt
➜ 0902 git:(main) bash while_command_output_condition_loop.sh
Baidu is reachable
192.0.2.1 www.baidu.com
Baidu is not reachable anymore
Found hello in data.txt. data.txt contents:
hello data.txt读取文件内容
bash
➜ 0902 git:(main) cat while_file_reading_loops.sh
#!/bin/bash
if [ -f "data.txt" ]; then
rm -f data.txt
fi
# sudo dnf install -y epel-release
# sudo dnf install -y pwgen
# 生成一个包含 5 行 16 个随机字符的文件
pwgen 16 5 >data.txt
# 使用 while 循环读取文件内容
while read -r line; do
echo "Line: $line"
done <data.txt
rm -f data.txt
➜ 0902 git:(main) bash while_file_reading_loops.sh
Line: saeRae8cah5aezei
Line: aidooXoov3Kooyie
Line: queeshee4OocoYi7
Line: aeveezeo4IephaiX
Line: ahK3Quie1fuaRaa2break 和 continue
break:用于立即退出循环。continue:用于跳过当前循环的剩余部分,直接进入下一次循环。
bash
➜ 0902 git:(main) cat while_nested_loops_break_continue.sh
#!/bin/bash
# while 实现双层循环
# 外层循环
outer_index=0
while [ $outer_index -le 6 ]; do
# 内层循环
inner_index=0
while [ $inner_index -lt 3 ]; do
# 内层循环使用 continue 跳过当前循环
if [ $inner_index -eq 1 ]; then
((inner_index++)) # 在跳过之前递增 inner_index
continue
fi
echo "Outer loop: $outer_index, Inner loop: $inner_index"
((inner_index++))
done
# 外层循环使用 break 退出循环
if [ $outer_index -eq 4 ]; then
break
fi
((outer_index += 2))
done
➜ 0902 git:(main) bash while_nested_loops_break_continue.sh
Outer loop: 0, Inner loop: 0
Outer loop: 0, Inner loop: 2
Outer loop: 2, Inner loop: 0
Outer loop: 2, Inner loop: 2
Outer loop: 4, Inner loop: 0
Outer loop: 4, Inner loop: 2until 循环
until循环是一种条件控制结构,它重复执行一系列命令,直到给定的条件为真时停止。until循环与while循环的主要区别在于条件的真伪:while循环在条件为真时运行,而until循环在条件为假时运行。
bash
until [ condition ]; do
commands
donecondition返回的退出状态码不是 0(即条件为假),则循环体内的commands将被执行。- 一旦
condition测试为真(返回退出状态码 0),循环就会停止。
简单的计数循环
bash
➜ 0902 git:(main) cat until_counter_loop.sh
#!/bin/bash
counter=0
until [ $counter -gt 5 ]; do # 循环将在 counter 大于 5 时停止
echo "Counter: $counter"
((counter++))
done
➜ 0902 git:(main) bash until_counter_loop.sh
Counter: 0
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5读取用户输入直到符合条件
bash
➜ 0902 git:(main) cat until_user_input.sh
#!/bin/bash
input=""
until [ "$input" == "exit" ]; do # 循环将在输入为 "exit" 时停止
read -p "Enter something (type 'exit' to quit): " input
echo "You entered: $input"
done
answer=""
until [[ "$answer" == "yes" || "$answer" == "no" ]]; do # 循环将在输入为 "yes" 或 "no" 时停止
read -p "Please enter 'yes' or 'no': " answer
echo "You entered: $answer"
done
➜ 0902 git:(main) bash until_user_input.sh
Enter something (type 'exit' to quit): ex
You entered: ex
Enter something (type 'exit' to quit): exitt
You entered: exitt
Enter something (type 'exit' to quit): exit
You entered: exit
Please enter 'yes' or 'no': noooo
You entered: noooo
Please enter 'yes' or 'no': ye
You entered: ye
Please enter 'yes' or 'no': yes
You entered: yes使用 until 循环等待文件存在
bash
➜ 0902 git:(main) cat until_file_exists.sh
#!/bin/bash
filename="data.txt"
index=0
until [ -e "$filename" ]; do # 循环将在文件存在时停止
echo "File $filename does not exist. Waiting..."
sleep 1
if [ $index -ge 3 ]; then
touch $filename
fi
((index++))
done
echo "File $filename exists!"
rm -f $filename
➜ 0902 git:(main) bash until_file_exists.sh
File data.txt does not exist. Waiting...
File data.txt does not exist. Waiting...
File data.txt does not exist. Waiting...
File data.txt does not exist. Waiting...
File data.txt exists!检查网络连接是否恢复
bash
➜ 0902 git:(main) cat until_network_check.sh
#!/bin/bash
# ping baidu.com 成功时返回 0,失败时返回非 0 值
until ping -c 1 baidu.com >/dev/null; do # 只要不能 ping 通 baidu.com,继续循环
echo "Baidu is not reachable"
sleep 2
done
echo "Baidu is reachable"
➜ 0902 git:(main) bash until_network_check.sh
Baidu is not reachable
Baidu is reachable环境变量
常见环境变量
| 变量名 | 描述 | 示例值 |
|---|---|---|
PATH | 决定了 shell 在执行命令时搜索的目录列表 | /usr/local/sbin:/usr/local/bin:/usr/bin |
HOME | 当前用户的主目录路径 | /root |
USER | 当前用户名 | root |
SHELL | 当前 shell 程序的路径 | /bin/zsh |
PWD | 当前工作目录 | /home/wwvl/VSCodeProject |
LANG | 系统语言和地区设定,用于处理不同语言的输出和格式 | en_US.UTF-8 |
EDITOR | 默认的文本编辑器 | nvim |
HOSTNAME | 当前机器的主机名 | VM-16-3-centos |
TERM | 终端类型 | xterm |
HISTFILE | 存储命令历史的文件路径 | /home/wwvl/.bash_history |
HISTSIZE | 命令历史记录的最大条数 | 50000 |
查看环境变量
bash
printenv
# 或
env设置环境变量
bash
# 使用 export 命令设置环境变量
export VAR_NAME="value"
# 设置 MY_VAR 变量
export MY_VAR="Hello, World!"在命令前临时设置环境变量,变量仅对当前命令有效。
bash
# 临时设置环境变量
VAR_NAME="value" command_to_run
# 设置 LANG 变量运行 ls 命令
# ls 命令只在当前命令行中使用 fr_FR.UTF-8 环境
LANG=fr_FR.UTF-8 ls # 临时设置环境变量删除环境变量
bash
# 使用 unset 命令删除环境变量
unset VAR_NAME
# 删除 MY_VAR 变量
unset MY_VAR持久化环境变量
为了使环境变量在 shell 会话之间持久存在,可以将其添加到用户的 shell 配置文件中(如 .bashrc(.zshrc) 或 .bash_profile),这样它们会在每次启动 shell 时自动加载。
bash
# 使用 nvim 编辑 .bashrc/.zshrc/.bash_profile 文件
nvim ~/.bashrc
# 在文件中添加环境变量
export MY_VAR="Persistent Value"
# 保存文件并退出
:wq
# 或者使用 echo 命令将环境变量添加到配置文件中
echo "export MY_VAR='Persistent Value'" >> ~/.bashrc
echo "export MY_VAR='Persistent Value'" | tee -a ~/.bashrc
# 使配置文件生效
source ~/.bashrc环境变量与 Shell 变量的区别
- 环境变量: 通过
export命令设置的变量,它们可以被当前 shell 及其子进程访问。 - Shell 变量: 没有被
export的变量,它们仅在当前 shell 会话中有效。
环境变量的应用场景
配置 PATH: 增加自定义命令或工具的路径。
bashexport PATH=$PATH:/custom/path配置代理服务器: 设置
http_proxy和https_proxy来配置网络代理。bashexport http_proxy="http://127.0.0.1:7890" export https_proxy="http://127.0.0.1:7890"配置编译器: 设置
CC和CXX环境变量来指定 C 和 C++ 编译器。bashexport CC=gcc export CXX=g++
bash 的执行方式
| 执行方式 | 描述 | 使用示例 | 适用场景 |
|---|---|---|---|
| 直接指定 Shell 解释器 | 添加 shebang 并赋予执行权限后,直接执行脚本。 | ./script.sh | 执行独立的、已配置好权限的脚本。 |
| 指定 Shell 解释器执行 | 不需要 shebang 或执行权限,通过解释器名称(如 bash)运行脚本。 | bash script.sh | 测试或运行临时脚本。 |
使用 source 或 . 命令执行 | 在当前 Shell 环境中执行脚本,不创建子 Shell。脚本中的变量和函数在当前环境中生效。 | source script.sh 或 . script.sh | 加载配置文件或设置环境变量。 |
使用 exec 命令执行 | 将当前 Shell 进程替换为脚本进程,不会创建新的子 Shell。 | exec ./script.sh | 替换当前进程,执行完毕后退出。 |
直接指定 Shell 解释器
通过在脚本顶部添加 shebang(通常是 #!/bin/bash 或 #!/bin/sh)指定脚本解释器,然后为脚本文件添加执行权限,最后直接执行脚本。
bash
# 创建脚本文件,并在文件开头添加 shebang
$ nvim script.sh
#!/bin/bash
echo "Hello, World!"
# 保存文件并退出
:wq
# 为脚本添加可执行权限
chmod +x script.sh
# 直接执行脚本
./script.sh说明
#!/bin/bash告诉系统使用/bin/bash作为解释器。- 需要
chmod +x来为脚本文件添加执行权限。 - 使用
./script.sh或者指定完整路径/path/to/script.sh来运行脚本。
指定 Shell 解释器执行
不需要给脚本添加执行权限,直接使用解释器名称(如 bash 或 sh)来执行脚本文件。
bash
# 创建脚本文件,在文件中添加内容
nvim script.sh
echo "Hello, World!"
# 保存文件并退出
:wq
# 指定 bash 解释器执行
bash script.sh
# 指定 sh 解释器执行
sh script.sh说明
bash或sh会直接调用脚本进行解释执行。- 适合测试或运行临时脚本,不需要为脚本添加执行权限。
使用 source 或 . 命令执行
使用 source 或 . 命令执行脚本,脚本中的所有命令会在当前 Shell 环境中执行,而不会创建子 Shell 进程。
bash
# 创建脚本文件,并在文件中添加内容
nvim script.sh
echo "Hello, World!"
# 保存文件并退出
:wq
# 使用 source 命令执行
source script.sh
# 使用 . 命令执行
. script.sh说明
- 使用
source或.命令执行时,所有在脚本中定义的变量或执行的命令都在当前 Shell 环境中生效,而不会影响到外部 Shell 环境(即,变量和函数定义将持续存在)。 - 如果使用
./script.sh或bash script.sh来运行,上述脚本中定义的MY_VAR变量不会在当前 Shell 中生效,因为它会在子 Shell 中执行。
使用 exec 命令执行
使用 exec 命令可以将当前 Shell 替换为执行的脚本进程,从而不会创建新的子进程。
bash
# 创建脚本文件,并在文件中添加内容
nvim script.sh
echo "This is executed by exec."
# 保存文件并退出
:wq
# 使用 exec 命令执行
exec ./script.sh说明
- 执行
exec ./script.sh后,当前 Shell 进程被替换为script.sh,脚本执行完毕后不会返回到原来的 Shell,会直接退出。 - 常用于终止当前 Shell 并切换到一个新进程执行。